#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
百度网盘文件操作模块
提供文件的基础操作功能
"""

import os
import json
import time
import hashlib
import logging
import requests
from typing import Dict, List, Optional, Any, Union, BinaryIO


class FileOperations:
    """百度网盘文件操作类"""
    
    def __init__(self, client):
        """初始化文件操作
        
        Args:
            client: 百度网盘客户端实例
        """
        self.client = client
        self.logger = client.logger
        self.session = client.session
        
        # 百度网盘API端点
        self.api_base = "https://pan.baidu.com/rest/2.0/xpan"
    
    def get_files(self, folder_path: str = "/", page: int = 1, 
                 page_size: int = 100) -> Optional[List[Dict[str, Any]]]:
        """获取文件列表
        
        Args:
            folder_path: 文件夹路径
            page: 页码
            page_size: 每页数量
        
        Returns:
            List[Dict[str, Any]]: 文件列表
        """
        if not self.client.check_login_status():
            return None
        
        url = f"{self.api_base}/file"
        params = {
            'method': 'list',
            'access_token': self.client.access_token,
            'dir': folder_path,
            'order': 'time',
            'desc': 1,
            'start': (page - 1) * page_size,
            'limit': page_size
        }
        
        try:
            response = self.session.get(url, params=params)
            if response.status_code == 200:
                result = response.json()
                if result.get('errno') == 0:
                    files = []
                    for item in result.get('list', []):
                        files.append({
                            'id': str(item.get('fs_id')),
                            'name': item.get('filename'),
                            'path': item.get('path'),
                            'size': item.get('size', 0),
                            'type': 'folder' if item.get('isdir') == 1 else 'file',
                            'md5': item.get('md5'),
                            'create_time': item.get('server_ctime'),
                            'modify_time': item.get('server_mtime'),
                            'parent_path': folder_path
                        })
                    return files
                else:
                    self.logger.error(f"获取文件列表失败: {result.get('errmsg')}")
            else:
                self.logger.error(f"获取文件列表失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"获取文件列表请求失败: {e}")
        
        return None
    
    def get_file_info(self, file_path: str) -> Optional[Dict[str, Any]]:
        """获取文件信息
        
        Args:
            file_path: 文件路径
        
        Returns:
            Dict[str, Any]: 文件信息
        """
        if not self.client.check_login_status():
            return None
        
        url = f"{self.api_base}/file"
        params = {
            'method': 'meta',
            'access_token': self.client.access_token,
            'fsids': f'[{file_path}]' if file_path.isdigit() else None,
            'path': file_path if not file_path.isdigit() else None
        }
        
        # 移除None值的参数
        params = {k: v for k, v in params.items() if v is not None}
        
        try:
            response = self.session.get(url, params=params)
            if response.status_code == 200:
                result = response.json()
                if result.get('errno') == 0 and result.get('list'):
                    item = result['list'][0]
                    return {
                        'id': str(item.get('fs_id')),
                        'name': item.get('filename'),
                        'path': item.get('path'),
                        'size': item.get('size', 0),
                        'type': 'folder' if item.get('isdir') == 1 else 'file',
                        'md5': item.get('md5'),
                        'create_time': item.get('server_ctime'),
                        'modify_time': item.get('server_mtime')
                    }
                else:
                    self.logger.error(f"获取文件信息失败: {result.get('errmsg')}")
            else:
                self.logger.error(f"获取文件信息失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"获取文件信息请求失败: {e}")
        
        return None
    
    def get_download_link(self, file_path: str) -> Optional[str]:
        """获取文件下载链接
        
        Args:
            file_path: 文件路径
        
        Returns:
            str: 下载链接
        """
        if not self.client.check_login_status():
            return None
        
        url = f"{self.api_base}/file"
        params = {
            'method': 'filemetas',
            'access_token': self.client.access_token,
            'fsids': f'[{file_path}]' if file_path.isdigit() else None,
            'path': file_path if not file_path.isdigit() else None,
            'dlink': 1
        }
        
        # 移除None值的参数
        params = {k: v for k, v in params.items() if v is not None}
        
        try:
            response = self.session.get(url, params=params)
            if response.status_code == 200:
                result = response.json()
                if result.get('errno') == 0 and result.get('list'):
                    return result['list'][0].get('dlink')
                else:
                    self.logger.error(f"获取下载链接失败: {result.get('errmsg')}")
            else:
                self.logger.error(f"获取下载链接失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"获取下载链接请求失败: {e}")
        
        return None
    
    def download_file(self, file_path: str, save_path: str = None) -> Union[bool, bytes, None]:
        """下载文件
        
        Args:
            file_path: 文件路径
            save_path: 保存路径，None表示返回文件内容
        
        Returns:
            Union[bool, bytes, None]: 保存到文件返回bool，返回内容返回bytes，失败返回None
        """
        download_link = self.get_download_link(file_path)
        if not download_link:
            return None
        
        try:
            # 百度网盘下载需要特殊的User-Agent
            headers = {
                'User-Agent': 'pan.baidu.com',
                'Referer': 'https://pan.baidu.com'
            }
            
            response = self.session.get(download_link, headers=headers, stream=True)
            if response.status_code == 200:
                if save_path:
                    # 保存到文件
                    os.makedirs(os.path.dirname(save_path), exist_ok=True)
                    with open(save_path, 'wb') as f:
                        for chunk in response.iter_content(chunk_size=8192):
                            f.write(chunk)
                    self.logger.info(f"文件下载成功: {save_path}")
                    return True
                else:
                    # 返回文件内容
                    return response.content
            else:
                self.logger.error(f"下载文件失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"下载文件请求失败: {e}")
        
        return None
    
    def create_folder(self, name: str, parent_path: str = "/") -> Optional[Dict[str, Any]]:
        """创建文件夹
        
        Args:
            name: 文件夹名称
            parent_path: 父文件夹路径
        
        Returns:
            Dict[str, Any]: 创建成功返回文件夹信息，失败返回None
        """
        if not self.client.check_login_status():
            return None
        
        folder_path = os.path.join(parent_path, name).replace('\\', '/')
        
        url = f"{self.api_base}/file"
        params = {
            'method': 'create',
            'access_token': self.client.access_token
        }
        data = {
            'path': folder_path,
            'isdir': 1,
            'rtype': 1
        }
        
        try:
            response = self.session.post(url, params=params, data=data)
            if response.status_code == 200:
                result = response.json()
                if result.get('errno') == 0:
                    self.logger.info(f"文件夹创建成功: {folder_path}")
                    return {
                        'id': str(result.get('fs_id')),
                        'name': name,
                        'path': folder_path,
                        'type': 'folder',
                        'create_time': int(time.time()),
                        'parent_path': parent_path
                    }
                else:
                    self.logger.error(f"创建文件夹失败: {result.get('errmsg')}")
            else:
                self.logger.error(f"创建文件夹失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"创建文件夹请求失败: {e}")
        
        return None
    
    def delete_file_or_folder(self, file_path: str) -> bool:
        """删除文件或文件夹
        
        Args:
            file_path: 文件或文件夹路径
        
        Returns:
            bool: 删除是否成功
        """
        if not self.client.check_login_status():
            return False
        
        url = f"{self.api_base}/file"
        params = {
            'method': 'filemanager',
            'access_token': self.client.access_token,
            'opera': 'delete'
        }
        data = {
            'filelist': json.dumps([file_path])
        }
        
        try:
            response = self.session.post(url, params=params, data=data)
            if response.status_code == 200:
                result = response.json()
                if result.get('errno') == 0:
                    self.logger.info(f"删除成功: {file_path}")
                    return True
                else:
                    self.logger.error(f"删除失败: {result.get('errmsg')}")
            else:
                self.logger.error(f"删除失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"删除请求失败: {e}")
        
        return False
    
    def rename_file_or_folder(self, old_path: str, new_name: str) -> bool:
        """重命名文件或文件夹
        
        Args:
            old_path: 原路径
            new_name: 新名称
        
        Returns:
            bool: 重命名是否成功
        """
        if not self.client.check_login_status():
            return False
        
        # 构建新路径
        parent_path = os.path.dirname(old_path)
        new_path = os.path.join(parent_path, new_name).replace('\\', '/')
        
        url = f"{self.api_base}/file"
        params = {
            'method': 'filemanager',
            'access_token': self.client.access_token,
            'opera': 'rename'
        }
        data = {
            'filelist': json.dumps([{
                'path': old_path,
                'newname': new_name
            }])
        }
        
        try:
            response = self.session.post(url, params=params, data=data)
            if response.status_code == 200:
                result = response.json()
                if result.get('errno') == 0:
                    self.logger.info(f"重命名成功: {old_path} -> {new_path}")
                    return True
                else:
                    self.logger.error(f"重命名失败: {result.get('errmsg')}")
            else:
                self.logger.error(f"重命名失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"重命名请求失败: {e}")
        
        return False
    
    def move_file(self, file_path: str, target_folder_path: str) -> bool:
        """移动文件
        
        Args:
            file_path: 文件路径
            target_folder_path: 目标文件夹路径
        
        Returns:
            bool: 移动是否成功
        """
        if not self.client.check_login_status():
            return False
        
        filename = os.path.basename(file_path)
        new_path = os.path.join(target_folder_path, filename).replace('\\', '/')
        
        url = f"{self.api_base}/file"
        params = {
            'method': 'filemanager',
            'access_token': self.client.access_token,
            'opera': 'move'
        }
        data = {
            'filelist': json.dumps([{
                'path': file_path,
                'dest': target_folder_path,
                'newname': filename
            }])
        }
        
        try:
            response = self.session.post(url, params=params, data=data)
            if response.status_code == 200:
                result = response.json()
                if result.get('errno') == 0:
                    self.logger.info(f"文件移动成功: {file_path} -> {new_path}")
                    return True
                else:
                    self.logger.error(f"移动文件失败: {result.get('errmsg')}")
            else:
                self.logger.error(f"移动文件失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"移动文件请求失败: {e}")
        
        return False
    
    def copy_file(self, file_path: str, target_folder_path: str, new_name: str = None) -> Optional[Dict[str, Any]]:
        """复制文件
        
        Args:
            file_path: 文件路径
            target_folder_path: 目标文件夹路径
            new_name: 新文件名，None表示保持原名
        
        Returns:
            Dict[str, Any]: 复制成功返回新文件信息，失败返回None
        """
        if not self.client.check_login_status():
            return None
        
        filename = new_name or os.path.basename(file_path)
        new_path = os.path.join(target_folder_path, filename).replace('\\', '/')
        
        url = f"{self.api_base}/file"
        params = {
            'method': 'filemanager',
            'access_token': self.client.access_token,
            'opera': 'copy'
        }
        data = {
            'filelist': json.dumps([{
                'path': file_path,
                'dest': target_folder_path,
                'newname': filename
            }])
        }
        
        try:
            response = self.session.post(url, params=params, data=data)
            if response.status_code == 200:
                result = response.json()
                if result.get('errno') == 0:
                    self.logger.info(f"文件复制成功: {file_path} -> {new_path}")
                    return {
                        'name': filename,
                        'path': new_path,
                        'type': 'file',
                        'parent_path': target_folder_path
                    }
                else:
                    self.logger.error(f"复制文件失败: {result.get('errmsg')}")
            else:
                self.logger.error(f"复制文件失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"复制文件请求失败: {e}")
        
        return None
    
    def calculate_md5(self, file_data: Union[str, bytes, BinaryIO]) -> str:
        """计算文件MD5值
        
        Args:
            file_data: 文件数据（文件路径、字节数据或文件对象）
        
        Returns:
            str: MD5值
        """
        md5_hash = hashlib.md5()
        
        if isinstance(file_data, str):
            # 文件路径
            with open(file_data, 'rb') as f:
                for chunk in iter(lambda: f.read(8192), b""):
                    md5_hash.update(chunk)
        elif isinstance(file_data, bytes):
            # 字节数据
            md5_hash.update(file_data)
        else:
            # 文件对象
            for chunk in iter(lambda: file_data.read(8192), b""):
                md5_hash.update(chunk)
        
        return md5_hash.hexdigest()